package cn.hy.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import static cn.hy.entity.Permission.*;
import static cn.hy.entity.Role.ADMIN;
import static cn.hy.entity.Role.MANAGER;
import static org.springframework.http.HttpMethod.*;


// 使用方式2 WebSecurityConfigurerAdapter  boot 3.0 已经删除
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfiguration {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;
    private final LogoutHandler logoutHandler;

    // HttpSecurity 通过自定义 SecurityFilterChain Bean 来实现
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable() //关闭csrf(跨域)
//                .headers().frameOptions().sameOrigin() // 允许来自同一来源的H2 控制台的请求  H2后台管理界面采用了iframe，而spring security默认是禁止了ifreame的使用，因此我们也需要针对h2开放对iframe的限制
//                .headers().frameOptions().disable()  // X-Frame-Options 头主要是为了防止站点被别人劫持，所以 iframe 将会在 Spring Security 中默认是拒绝设置的。
//                .and()
                .authorizeHttpRequests()
                //配置需要放行的路径 路径匹配不是ant 风格
                .requestMatchers(
                        "/api/v1/auth/**",
                        "/v2/api-docs",
                        "/v3/api-docs",
                        "/v3/api-docs/**",
                        "/swagger-resources",
                        "/swagger-resources/**",
                        "/configuration/ui",
                        "/configuration/security",
                        "/swagger-ui/**",
                        "/webjars/**",
                        "/swagger-ui.html",
                        "/h2-console", // 没办法解决 h2 数据访问方式
                        "/h2-console/**"
                )
                .permitAll() //放行上述的所有路径
                /*
                 * 权限校验(需要登录的用户有指定的权限才可以)
                 * requestMatchers: 指定需要拦截的路径
                 * hasAnyAuthority: 指定需要的权限
                 */
                .requestMatchers("/api/v1/management/**").hasAnyRole(ADMIN.name(), MANAGER.name())
                .requestMatchers(GET, "/api/v1/management/**").hasAnyAuthority(ADMIN_READ.name(), MANAGER_READ.name())
                .requestMatchers(POST, "/api/v1/management/**").hasAnyAuthority(ADMIN_CREATE.name(), MANAGER_CREATE.name())
                .requestMatchers(PUT, "/api/v1/management/**").hasAnyAuthority(ADMIN_UPDATE.name(), MANAGER_UPDATE.name())
                .requestMatchers(DELETE, "/api/v1/management/**").hasAnyAuthority(ADMIN_DELETE.name(), MANAGER_DELETE.name())
                .anyRequest()
                .authenticated() //设置所有的请求都需要验证
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //使用无状态Session
                .and()
                .authenticationProvider(authenticationProvider)
                //添加jwt过滤器
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
                //设置logout(当调用这个接口的时候, 会调用logoutHandler的logout方法)
                .logout()
                .logoutUrl("/api/v1/auth/logout")
                .addLogoutHandler(logoutHandler)
                .logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext())
        ;

        return http.build();
    }


    /**
     * 配置 WebSecurity，可以通过 WebSecurityCustomizer Bean 来实现。
     *      You are asking Spring Security to ignore Mvc [pattern='/h2-console/**'].
     *      This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.
     */
//    @Bean
//    public WebSecurityCustomizer webSecurityCustomizer() {
//        return (web) -> web.ignoring().requestMatchers("/h2-console/**");
//    }
}
